/*
 * Created on 2005-3-23
 *
 * Copyright: Bluejay Networks Inc. 2005
 *
 * This software is the proprietary information of Bluejay Networks Inc. 
 * 
 * Use is subject to license terms.
 * 
 */
package com.ims.iaa;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.RemoteException;
import java.util.LinkedList;
import java.util.List;

import com.ims.iaa.IaaDebug;
import com.ims.iaa.IaaUtil;
import com.ims.iaa.AD.AdDataLink;

/**
 * @author ShengMengBai
 * 
 *  
 */
public class ImpReconnector {
    private final static int QUERY_INTERVAL = 300000;//5 minute.

    private final static int WAITING_CONNECTOR = 200;

    private final static int CONNECTOR_COUNT = IaaDebug.m_iSpiderAuxThread;

    private static Thread m_connectTread = null;

    private static SpiderDBConnection m_sp = null;

    private static LinkedList m_lstConnector = null;

    private static volatile boolean m_conRun = true;

    private static int m_iConInterval = 12;

    private static int m_iMaxConTimes = 5;

    public static void main(String[] args) {
        //get the parameter
        parseParameter(args);

        try {
            Class.forName("com.ims.iaa.IaaDebug");
        } catch (Exception e) {
        }

        m_sp = IaaUtil.getSpiderDBConnection(IaaDebug.ServerHost,
                IaaDebug.ServerPort);
        startReconnect();
        System.out.println("Bad URL reconnector: Enter <EXIT> to end.");

        BufferedReader buff = new BufferedReader(new InputStreamReader(
                System.in));
        String input = "";
        while (!input.toUpperCase().equals("EXIT")) {
            try {
                input = buff.readLine();
                if (!input.toUpperCase().equals("EXIT")) {
                    m_connectTread.interrupt();
                }
            } catch (IOException ioException) {
            }
        }
        m_conRun = false;
        m_connectTread.interrupt();
        IaaUtil.log("Exit by user");
        System.exit(0);
    }

    private static void parseParameter(String[] args) {
        String sHours = null;
        String sTimes = null;
        for (int i = 0; i < args.length; i++) {
            if (args[i].startsWith("-h:")) {
                sHours = args[i].substring(3);
            } else if (args[i].startsWith("-t:")) {
                sTimes = args[i].substring(3);
            }
        }
        if (sHours == null || sTimes == null) {
            usageInfo();
            System.exit(0);
        }
        try {
            m_iConInterval = Integer.parseInt(sHours);
            m_iMaxConTimes = Integer.parseInt(sTimes);
            if (m_iConInterval < 1) {
                System.out.println("The hours in -h:hours must larger than 0.");
                System.exit(0);
            }
            if (m_iMaxConTimes < 1) {
                System.out.println("The times in -t:times must larger than 0.");
                System.exit(0);
            }
            System.out.println("Start ImpReConnector wiht parameter: -h:" + sHours + " -t:" + sTimes);
            System.out.println();
        } catch (NumberFormatException e) {
            usageInfo();
            System.exit(0);
        }
    }

    private static void usageInfo() {
        System.out.println("The programe receive 2 parameters: -h:hours -t:times\r\n");
        System.out.println("\t\t-t:hours, the value of 'hours' is the reconnect inteval, unit is hours.\r\n");
        System.out.println("\t\t          such as -h:12, if connect to a URL failed, econnect it after 12 hours.\r\n");
        System.out.println("\t\t-t:times, the max reconnect times.\r\n");
        System.out.println("\t\t          such as -t:5, don't reconnect to the URL more than 5 times.\r\n");
    }

    /**
     * Start the Reconnect thread and will auto query database and reconenc to
     * the resouce. this method only
     *  
     */
    private static void startReconnect() {
        IaaUtil.log("Start bad impression reconnect thread.");
        //new AdDataReConnector list.
        m_lstConnector = new LinkedList();
        for (int i = 0; i < CONNECTOR_COUNT; i++) {
            m_lstConnector.add(new AdDataReConnector(m_sp, i + 1));
        }
        //new reconnected thread
        m_connectTread = new IaaThread("Reconnector_main") {
            public void run() {
                while (m_conRun) {
                    try {
                        //get the impession to re-connect.
                        List lstRetry = m_sp.getReConnectImp(m_iMaxConTimes,
                                m_iConInterval, IaaThread.getLogString());
                        //if found reconnect resource, reconnect it, else
                        // sleep.
                        if (lstRetry.size() > 0) {
                            for (int i = 0; i < lstRetry.size(); i++) {
                                AdDataLink objLink = (AdDataLink) lstRetry.get(i);
                            }
                            while (lstRetry.size() > 0) {
                                //accquire a connector
                                AdDataReConnector con = acquireConnector();
                                if (con != null) {
                                    //connect
                                    AdDataLink objLink = (AdDataLink) lstRetry.get(0);
                                    lstRetry.remove(0);
                                    con.connect(objLink);
                                } else {
                                    try {
                                        sleep(WAITING_CONNECTOR);
                                    } catch (InterruptedException e) {}
                                }
                            }
                        } else {
                            try {
                                System.out.println("No reconnected data in database, wait 10 mins, press ENTER to wake up me.");
                                Thread.sleep(600000);
                            } catch (InterruptedException e) {
                                System.out.println("waked up.");
                            }
                        }
                    } catch (RemoteException e) {
                        IaaUtil.printStackTrace(e);
                    } catch (Exception e) {
                        IaaUtil.printStackTrace(e);
                    }
                }
            }
        };
        m_connectTread.start();
    }

    /**
     * register a free AdDataConnector object to the pool.
     * 
     * @param con
     *            AdDataConnector object
     */
    private static AdDataReConnector acquireConnector() {
        AdDataReConnector con = null;
        synchronized (m_lstConnector) {
            for (int i = 0; i < m_lstConnector.size(); i++) {
                con = (AdDataReConnector) m_lstConnector.get(i);
                if (!con.isConnecting()) {
                    break;
                } else {
                    //if is timeout, stop the thread and reset the connector(it
                    // will create a new thread at next time start it).
                    if (con.isTimeOut()) {
                        con.stopConnect();
                        m_lstConnector.remove(i);
                        con = new AdDataReConnector(m_sp, i + 1);
                        m_lstConnector.add(i, con);
                        break;
                    }
                    con = null;
                }
            }
        }
        return con;
    }
}
/**
 * This class connect to a ad data(such as image, flash or applet) in a single
 * thread at one time.
 */

class AdDataReConnector {
    //if the the connector hasn't task, it will sllep SLEEP_TIME until
    // interrupt
    private static final int SLEEP_TIME = 300000;//5 minutes

    //if the the time of connector connect to the resource larger than
    // TIME_OUT, it is timeout.
    private static final int TIME_OUT = 360000;//6 minutes

    private LinkedList m_lstAgency;
    private LinkedList m_lstAdSize;

    private SpiderDBConnection m_sp;

    //the conector thread
    private Thread m_conThread = null;

    //if m_bContinue=true, the thread will always run, and waiting for the
    // task,
    //else the thread will stop, and next time start the thread will create a
    // new thread
    private volatile boolean m_bContinue = false;

    //if the thread is connecting to the resouce, the m_bNewTask is true,
    //if the thread is waiting for task, the m_bNewTask is false.
    private volatile boolean m_bNewTask = false;

    //record the task start time or the thread last wake up time.
    private volatile long m_lLastAvtive = System.currentTimeMillis();

    //the task
    AdDataLink m_ad = null;

    //the AdDataReConnector index
    private int m_iIndex = -1;

    public AdDataReConnector(SpiderDBConnection sp, int iIndex) {
        m_sp = sp;
        m_iIndex = iIndex;
        try {
            m_lstAgency = m_sp.loadAgency(IaaThread.getLogString(),m_iIndex);
            m_lstAdSize = m_sp.loadAdSize(IaaThread.getLogString(),m_iIndex);
        } catch (RemoteException e) {
            m_lstAgency = new LinkedList();
            m_lstAdSize = new LinkedList();
        }
    }

    public void connect(AdDataLink ad) {
        m_ad = ad;
        startConnect();
    }

    /**
     * If the connector connect to the resource morethan TIME_OUT, will return
     * true, else false.
     * 
     * @return
     */
    public boolean isTimeOut() {
        long lInterval = System.currentTimeMillis() - m_lLastAvtive;
        if (lInterval > TIME_OUT) {
            return true;
        }
        return false;
    }

    /**
     * @return true, if the connector is conenct the resource, else false.
     */
    public boolean isConnecting() {
        return m_bNewTask;
    }

    /**
     * Stop current Reconect thread, in some insatnce, the thread may be block
     * too long time, so, you need stop it
     * 
     * @return, if stop successful, return true, else false.
     * 
     * @see #isTimeOut()
     */
    public boolean stopConnect() {
        try {
            m_bContinue = false;
            m_conThread.interrupt();
            m_bNewTask = false;
            return true;
        } catch (Exception e) {
            IaaUtil.printStackTrace(e);
            return false;
        }
    }

    private void startConnect() {
        class MyThread extends IaaThread {
            public MyThread(String sName) {
                super(sName);
            }

            public void run() {
                while (m_bContinue) {
                    m_bNewTask = true;
                    m_lLastAvtive = System.currentTimeMillis();
                    try {
                        //if the data need save, save it.
                        //this judge delete by ShengMengBai at 20045-02-28,
                        // because we must update the state.
                        //if(m_ad.parse(m_lstAgency)){
                        IaaUtil.log("Start reconnect: " + m_ad.getSrcURL());
                        m_ad.parse(m_lstAgency,m_lstAdSize);
                        //update the resouce
                        m_sp.updateAdDataLink(m_ad, toString());
                        IaaUtil.log("End reconnect: " + m_ad.getSrcURL());
                        //}
                    } catch (Exception e) {
                        IaaUtil.printStackTrace(e);
                    }
                    //register this AdDataConnector.
                    m_bNewTask = false;
                    //m_ImpReconnector.releaseConnector(m_owner);
                    //waiting for new task
                    while (m_bContinue && !m_bNewTask) {
                        try {
                            m_lLastAvtive = System.currentTimeMillis();
                            sleep(SLEEP_TIME);
                        } catch (Exception e) {}
                    }
                }
            }
        }
        ;
        //the thread isn't runing, new thread and run it
        if (m_bContinue == false) {
            m_bContinue = true;
            m_conThread = new MyThread("Reconnector_aux" + m_iIndex);
            m_bNewTask = true;
            m_conThread.start();
        } else {//thread is sleep, wake it up.
            try {
                m_bNewTask = true;
                m_conThread.interrupt();
            } catch (Exception e) {
                IaaUtil.printStackTrace(e);
            }
        }
    }
}